package com.uuah.server.config;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;

import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.api.lifecycle.IStartable;
import com.uuah.api.lifecycle.Initialisable;
import com.uuah.config.i18n.CoreMessages;
import com.uuah.exception.lifecycle.FatalException;
import com.uuah.server.UuahServer;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.config.IUuahConfiguration;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.utils.FileUtils;
import com.uuah.utils.NumberUtils;
import com.uuah.utils.StringUtils;
import com.uuah.utils.UUID;

public class UuahConfiguration implements IUuahConfiguration {
	protected transient Logger logger = LoggerFactory
			.getLogger(UuahConfiguration.class);
	private boolean synchronous = false;

	private String encoding = "UTF-8";

	private int synchronousEventTimeout = 10000;

	private boolean remoteSync = false;

	private String workingDirectory = "./.uuah";

	private String id;

	private String domainId;

	private boolean cacheMessageAsBytes = true;

	private boolean cacheMessageOriginalPayload = true;

	private boolean autoWrapMessageAwareTransform = true;

	public UuahConfiguration() {
		super();

		applySystemProperties();

		if (id == null) {
			id = UUID.getTimeBasedUUIDString();
		}
		if (domainId == null) {
			try {
				domainId = InetAddress.getLocalHost().getHostName();
			} catch (UnknownHostException e) {
				logger.warn(e.getMessage());
				domainId = "com.uuah.server";
			}
		}

		try {
			validateEncoding();
			validateXML();
		} catch (FatalException e) {
			throw new RuntimeException(e);
		}
	}

	protected void applySystemProperties() {
		String p;

		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "encoding");
		if (p != null) {
			encoding = p;
		}
		p = System
				.getProperty(SYSTEM_PROPERTY_PREFIX + "endpoints.synchronous");
		if (p != null) {
			synchronous = BooleanUtils.toBoolean(p);
		}
		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "timeout.synchronous");
		if (p != null) {
			synchronousEventTimeout = NumberUtils.toInt(p);
		}
		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "workingDirectory");
		if (p != null) {
			workingDirectory = p;
		}
		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "serverId");
		if (p != null) {
			id = p;
		}
		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "domainId");
		if (p != null) {
			domainId = p;
		}
		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "message.cacheBytes");
		if (p != null) {
			cacheMessageAsBytes = BooleanUtils.toBoolean(p);
		}
		p = System
				.getProperty(SYSTEM_PROPERTY_PREFIX + "message.cacheOriginal");
		if (p != null) {
			cacheMessageOriginalPayload = BooleanUtils.toBoolean(p);
		}
		p = System.getProperty(SYSTEM_PROPERTY_PREFIX + "transform.autoWrap");
		if (p != null) {
			autoWrapMessageAwareTransform = BooleanUtils.toBoolean(p);
		}
	}

	protected void validateEncoding() throws FatalException {
		if (!Charset.isSupported(encoding)) {
			throw new FatalException(CoreMessages.propertyHasInvalidValue(
					"encoding", encoding), this);
		}
	}

	protected void validateXML() throws FatalException {
		SAXParserFactory f = SAXParserFactory.newInstance();
		if (f == null || f.getClass().getName().indexOf("crimson") != -1) {
			throw new FatalException(ServerMessages
					.valueIsInvalidFor(f.getClass().getName(),
							"javax.xml.parsers.SAXParserFactory"), this);
		}
	}

	public boolean isDefaultSynchronousEndpoints() {
		return synchronous;
	}

	public void setDefaultSynchronousEndpoints(boolean synchronous) {
		if (verifyContextNotStarted()) {
			this.synchronous = synchronous;
		}
	}

	public int getDefaultSynchronousEventTimeout() {
		return synchronousEventTimeout;
	}

	public void setDefaultSynchronousEventTimeout(int synchronousEventTimeout) {
		if (verifyContextNotStarted()) {
			this.synchronousEventTimeout = synchronousEventTimeout;
		}
	}

	public String getWorkingDirectory() {
		return workingDirectory;
	}

	public String getUuahHomeDirectory() {
		return System.getProperty(Constants.UUAH_HOME_DIRECTORY_PROPERTY);
	}

	public void setWorkingDirectory(String workingDirectory) {
		if (verifyContextNotInitialized()) {
			this.workingDirectory = FileUtils.newFile(workingDirectory)
					.getAbsolutePath().replaceAll("\\\\", "/");
		}
	}

	public String getDefaultEncoding() {
		return encoding;
	}

	public void setDefaultEncoding(String encoding) {
		if (verifyContextNotInitialized()) {
			this.encoding = encoding;
		}
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		if (verifyContextNotInitialized()) {
			if (StringUtils.isBlank(id)) {
				throw new IllegalArgumentException(
						"Cannot set server id to null/blank");
			}
			this.id = id;
		}
	}

	public String getDomainId() {
		return domainId;
	}

	public void setDomainId(String domainId) {
		if (verifyContextNotInitialized()) {
			this.domainId = domainId;
		}
	}

	public String getSystemName() {
		return domainId + "." + id;
	}

	public boolean isAutoWrapMessageAwareTransform() {
		return autoWrapMessageAwareTransform;
	}

	public void setAutoWrapMessageAwareTransform(
			boolean autoWrapMessageAwareTransform) {
		if (verifyContextNotStarted()) {
			this.autoWrapMessageAwareTransform = autoWrapMessageAwareTransform;
		}
	}

	public boolean isCacheMessageAsBytes() {
		return cacheMessageAsBytes;
	}

	public void setCacheMessageAsBytes(boolean cacheMessageAsBytes) {
		if (verifyContextNotStarted()) {
			this.cacheMessageAsBytes = cacheMessageAsBytes;
		}
	}

	public boolean isCacheMessageOriginalPayload() {
		return cacheMessageOriginalPayload;
	}

	public void setCacheMessageOriginalPayload(
			boolean cacheMessageOriginalPayload) {
		if (verifyContextNotStarted()) {
			this.cacheMessageOriginalPayload = cacheMessageOriginalPayload;
		}
	}

	protected boolean verifyContextNotInitialized() {
		IUuahContext context = UuahServer.getUuahContext();
		if (context != null
				&& context.getLifecycleManager().isPhaseComplete(
						Initialisable.PHASE_NAME)) {
			logger
					.warn("Cannot modify UuahConfiguration once the UuahContext has been initialized.  Modification will be ignored.");
			return false;
		} else {
			return true;
		}
	}

	protected boolean verifyContextNotStarted() {
		IUuahContext context = UuahServer.getUuahContext();
		if (context != null
				&& context.getLifecycleManager().isPhaseComplete(
						IStartable.PHASE_NAME)) {
			logger
					.warn("Cannot modify UuahConfiguration once the UuahContext has been started.  Modification will be ignored.");
			return false;
		} else {
			return true;
		}
	}
}
